﻿using Autodesk.AutoCAD.DatabaseServices;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;

namespace AutoCadConvert
{
    class Area:BspObject
    {
        public List<Point> m_LogicPoints = null;
        public string Name = "";
        public Color Color;
        public bool ShowName = false;
        public bool IsCreateSkirtingLine = false;//是否有踢脚线
        public bool IsCreateTopLine = false;//是否有吊顶

        public IEnumerable<XElement> ChildXml()
        {
            if (m_LogicPoints != null)
                foreach (Point item in m_LogicPoints)
                    yield return new XElement("_" + m_LogicPoints.IndexOf(item), item.index);
            else
                yield return null;
        }
        public override IEnumerable<XElement> ToXml()
        {
            if (Name == "厕所" || Name == "厨房")
            {
                IsCreateSkirtingLine = false;
                IsCreateTopLine = false;
            }

            return base.ToXml().Concat(new XElement[] {
                new XElement("LogicPos",ChildXml()),
                new XElement("Name",Name),
                new XElement("Color",Color.ToString()),
                new XElement("ShowName",ShowName?1:0),
                new XElement("IsCreateSkirtingLine",IsCreateSkirtingLine?1:0),
                new XElement("IsCreateTopLine",IsCreateTopLine?1:0)
            });
        }
        public Point Center
        {
            get
            {
                Point ptCenter = new Point();
                int i, j;
                double ai, atmp = 0, xtmp = 0, ytmp = 0;
                if (m_LogicPoints.Count == 1)
                    return m_LogicPoints[0];
                if ((m_LogicPoints.Count == 2) || (m_LogicPoints.Count == 3 && m_LogicPoints[0] == m_LogicPoints[2]))
                    return new Point() { X = (m_LogicPoints[1].X + m_LogicPoints[0].X) / 2, Y=(m_LogicPoints[1].Y + m_LogicPoints[0].Y) / 2};

                int n = m_LogicPoints.Count;
                for (i = n - 1, j = 0; j < n; i = j, j++)
                {
                    ai = m_LogicPoints[i].X * m_LogicPoints[j].Y - m_LogicPoints[j].X * m_LogicPoints[i].Y;
                    atmp += ai;
                    xtmp += (m_LogicPoints[j].X + m_LogicPoints[i].X) * ai;
                    ytmp += (m_LogicPoints[j].Y + m_LogicPoints[i].Y) * ai;
                }

                if (atmp != 0)
                {
                    ptCenter.X = Convert.ToInt32(xtmp / (3 * atmp));
                    ptCenter.Y = Convert.ToInt32(ytmp / (3 * atmp));

                }
                return ptCenter;
            }
        }
        public static bool isInPolygon(List<Point> ptLst,Point point)
        {//点是否在多边形内
            int nCross = 0;
            for (int i = 0; i < ptLst.Count; i++)
            {
                Point p1 = ptLst[i];
                Point p2 = ptLst[(i + 1) % ptLst.Count];
                // 求解 y=p.y 与 p1 p2 的交点
                // p1p2 与 y=p0.y平行
                if (p1.Y == p2.Y)
                    continue;
                // 交点在p1p2延长线上
                if (point.Y < Math.Min(p1.Y, p2.Y))
                    continue;
                // 交点在p1p2延长线上
                if (point.Y >= Math.Max(p1.Y, p2.Y))
                    continue;
                // 求交点的 X 坐标
                double x = (double)(point.Y - p1.Y) * (double)(p2.X - p1.X)
                        / (double)(p2.Y - p1.Y) + p1.X;
                // 只统计单边交点
                if (x > point.X)
                    nCross++;
            }
            return (nCross % 2 == 1);
            //int crossings = 0;
            //for (int i = 0; i < ptLst.Count; i++)
            //{
            //    double slope = (ptLst[(i+1)%ptLst.Count].Y - ptLst[i].Y) / (ptLst[(i + 1) % ptLst.Count].X - ptLst[i].X);
            //    bool cond1 = (ptLst[i].X < point.X) && (ptLst[(i + 1) % ptLst.Count].X > point.X);
            //    bool cond2 = (ptLst[(i + 1) % ptLst.Count].X < point.X) && (ptLst[i].X > point.X);
            //    bool above = (point.Y < slope * (point.X - ptLst[i].X) + ptLst[i].Y);
            //    if ((cond1 || cond2) && above)
            //    {
            //        crossings++;
            //    }
            //}
            //return (crossings % 2 != 0);
        }
        public static bool isInBoundary(List<Point> ptLst, Point point)
        {
            for (int i = 0; i < ptLst.Count(); i++)
            {
                Point p1 = ptLst[i];
                Point p2 = ptLst[(i + 1) % ptLst.Count];
                // 取多边形任意一个边,做点point的水平延长线,求解与当前边的交点个数

                // point 在p1p2 底部 --> 无交点
                if (point.Y < Math.Min(p1.Y, p2.Y))
                    continue;
                // point 在p1p2 顶部 --> 无交点
                if (point.Y > Math.Max(p1.Y, p2.Y))
                    continue;

                // p1p2是水平线段,要么没有交点,要么有无限个交点
                if (p1.Y == p2.Y)
                {
                    double minX = Math.Min(p1.X, p2.X);
                    double maxX = Math.Max(p1.X, p2.X);
                    // point在水平线段p1p2上,直接return true
                    if (point.Y == p1.Y && (point.X >= minX && point.X <= maxX))
                    {
                        return true;
                    }
                }
                else
                { // 求解交点
                    Line ln1 = new Line() { V0 = new Point() { X = p2.X, Y = p2.Y }, V1 = new Point() { X = p1.X, Y = p1.Y } };
                    Line ln2 = new Line() { V0 = new Point() { X = point.X, Y = point.Y }, V1 = new Point() { X = p1.X, Y = p1.Y } };
                    double xc = ln1.Cross(ln2) / ln1.Distance / ln2.Distance;
                    if (Math.Abs(xc) < 0.1) // 当x=point.x时,说明point在p1p2线段上
                        return true;
                }
            }
            return false;
        }
        public static double GetArea(List<Point> points)
        {
            double area2 = 0;
            for (int numPoint = 0; numPoint < points.Count - 1; numPoint++)
            {
                Point point = points[numPoint];
                Point nextPoint = points[numPoint + 1];
                area2 += point.X * nextPoint.Y - point.Y * nextPoint.Y;
            }
            return area2 / 2;
        }
    }
    class Areas:Collection
    {
        private List<Space> m_Space = new List<Space>();
        private List<Line> m_Lines = new List<Line>();
        public  List<Area> m_Areas = new List<Area>();
        public Areas(House house):base(house)
        {
        }
        public override void AddData(Entity value)
        {
            //添加点数据
            List<Point> ptLst = m_house.Item<Points>().m_Points;
            m_house.Item<Lines>().AddData(value);
            if (value is DBText)
            {
                DBText text = value as DBText;
                m_Space.Add(new Space()
                {
                    Name = text.TextString,
                    m_Point = new Point(text.Position),
                });

            }
            else if (value is MText)
            {
                MText text = value as MText;
                m_Space.Add(new Space()
                {
                    Name = text.Contents,
                    m_Point = new Point(text.GetBoundingPoints()[0]),
                });
            }
        }
        public override void AjustLineAndPoint()
        {
            Point center = m_house.Item<Points>().m_Center;
            List<Point> pt = m_Space.Select(o=>o.m_Point- center).ToList();
            List<List<Point>> tmpArea = new List<List<Point>>(),openArea = new List<List<Point>>();
            List<Line> lnAll = m_house.Item<Lines>().m_Lines;
            List<Point> ptAll = m_house.Item<Points>().m_Points;
            //拆分点和线
            Lines.RecursionCrossLine(lnAll, ptAll);
            //寻找区域
            //RecursionLine(lnAll.Clone<Line>().ToList(), tmpArea, openArea);
            for (int i = 0; i < pt.Count; ++i)
            {
                List<Point> Area = new List<Point>();
                if (FindArea(pt[i], ptAll.Clone<Point>().ToList(), lnAll.Clone<Line>().ToList(), Area))
                {
                    tmpArea.Add(Area);
                }
            }
            //构造区域
            foreach (List<Point> ptls in tmpArea)
            {
                ptls.ForEach(o =>
                {
                    o.index = ptAll.IndexOf(o);
                    o.refCount = lnAll.Where(t => t.V0 == o || t.V1 == o).Count();
                });

                string textName = "";
                foreach (Space sp in m_Space)
                    if (Area.isInPolygon(ptls, sp.m_Point-center))
                        textName = sp.Name;

                m_Areas.Add(new Area() {
                    m_LogicPoints = ptls,
                    Name = textName,
                    Color = new Color(0.5, 0.5, 0.6, 0.6),
                    ShowName = false,
                    IsCreateSkirtingLine = textName == "厨房" || textName == "厕所" ? false : false,
                    IsCreateTopLine = textName == "厨房" || textName == "厕所" ? false : false,
                });
            }
        }
        public bool FindArea(Point hotpoint, List<Point> PtAll, List<Line> lnAll, List<Point> Area)
        {
            bool haveIsolated = true;
            Point Start = hotpoint;
            Point End = null;
            Point Next = new Point();
            double MinDis = 10000000;
            while (haveIsolated)
            {
                haveIsolated = false;
                for (int i = 0; i < lnAll.Count; ++i)
                {
                    Line ln = lnAll[i];
                    if (ln.V0.refCount == 0 || ln.V0.refCount == 1 || ln.V1.refCount == 0 || ln.V1.refCount == 1)
                    {
                        haveIsolated = true;
                        ln.V0.refCount--;
                        ln.V1.refCount--;
                        lnAll.RemoveAt(i);
                    }
                }
            }
            for (int i = 0; i < lnAll.Count; ++i)
            {
                Line ln = new Line();
                ln.V0 = Start;
                ln.V1 = lnAll[i].V0;
                if (ln.Distance < MinDis && ln.Distance > 0)
                {
                    MinDis = ln.Distance;
                    End = ln.V1;
                }
                ln.V0 = Start;
                ln.V1 = lnAll[i].V1;
                if (ln.Distance < MinDis && ln.Distance > 0)
                {
                    MinDis = ln.Distance;
                    End = ln.V1;
                }
            }
            if (End != null)
            {
                bool ret = true;
                Area.Add(End);
                do
                {
                    ret = FindNextPoint(Start, End, ref Next, PtAll, lnAll);
                    Start = End;
                    End = Next;
                    Area.Add(End);
                } while (ret && Next != Area[0]);
            }
            if (Area.Count >= 4 && Area[0] == Area[Area.Count - 1])
            {
                Area.RemoveAt(Area.Count - 1);
                return true;
            }
            return false;
        }
        public bool FindNextPoint(Point Start, Point End, ref Point Next, List<Point> PtAll, List<Line> lnAll)
        {
            List<Point> tmp = new List<Point>();
            Dictionary<Point, double> PtdMap = new Dictionary<Point, double>();
            double MaxCross = -2;
            Point SelectPoint = null;
            for (int i = 0; i < lnAll.Count; ++i)
            {
                if (lnAll[i].V0 == End && lnAll[i].V1 != Start)
                {
                    tmp.Add(lnAll[i].V1);
                }
                if (lnAll[i].V1 == End && lnAll[i].V0 != Start)
                {
                    tmp.Add(lnAll[i].V0);
                }
            }
            for (int i = 0; i < tmp.Count; ++i)
            {
                Line lntmps = new Line();
                lntmps.V0 = Start;
                lntmps.V1 = End;
                lntmps = lntmps.Normal();
                Line lntmpe = new Line();
                lntmpe.V0 = End;
                lntmpe.V1 = tmp[i];
                lntmpe = lntmpe.Normal();
                double cross = lntmps.Cross(lntmpe);
                PtdMap.Add(tmp[i], cross);
                if (cross > MaxCross)
                {
                    MaxCross = cross;
                    SelectPoint = tmp[i];
                }
            }
            if (MaxCross > 0)
            {
                double min = 2;
                foreach (KeyValuePair<Point, double> kvp in PtdMap)
                {
                    if (kvp.Value > 0)
                    {
                        Line lntmps = new Line();
                        lntmps.V0 = Start;
                        lntmps.V1 = End;
                        lntmps = lntmps.Normal();
                        Line lntmpe = new Line();
                        lntmpe.V0 = End;
                        lntmpe.V1 = kvp.Key;
                        lntmpe = lntmpe.Normal();
                        double DotTmp = lntmps.Dot(lntmpe);
                        if (DotTmp < min)
                        {
                            min = DotTmp;
                            SelectPoint = kvp.Key;

                        }
                    }
                }
                Next = SelectPoint;
                return true;
            }
            if (MaxCross == 0)
            {
                foreach (KeyValuePair<Point, double> kvp in PtdMap)
                {
                    if (kvp.Value == 0)
                    {
                        SelectPoint = kvp.Key;
                    }
                }
                Next = SelectPoint;
                return true;
            }
            if (MaxCross < 0 && MaxCross > -2)
            {
                double max = -2;
                foreach (KeyValuePair<Point, double> kvp in PtdMap)
                {
                    if (kvp.Value < 0)
                    {
                        Line lntmps = new Line();
                        lntmps.V0 = Start;
                        lntmps.V1 = End;
                        lntmps = lntmps.Normal();
                        Line lntmpe = new Line();
                        lntmpe.V0 = End;
                        lntmpe.V1 = kvp.Key;
                        lntmpe = lntmpe.Normal();
                        double DotTmp = lntmps.Dot(lntmpe);
                        if (DotTmp > max)
                        {
                            max = DotTmp;
                            SelectPoint = kvp.Key;
                        }
                    }
                }
                Next = SelectPoint;
                return true;
            }
            return false;
        }
        public void DeleteLine(List<Line> lnAll, List<Line> ln)
        {
            foreach (Line line in ln)
            {
                //删除最上边的线
                if (lnAll.Contains(line))
                {
                    List<Line> tmp = lnAll.Where(o => o != line && line.V0.X >= Math.Min(o.V0.X, o.V1.X) && line.V0.X <= Math.Max(o.V0.X, o.V1.X)
                        || line.V1.X >= Math.Min(o.V0.X, o.V1.X) && line.V1.X <= Math.Max(o.V0.X, o.V1.X)).ToList();
                    if (tmp.All(o => o.V0.Y <= line.V1.Y && o.V0.Y <= line.V1.Y&& o.V1.Y <= line.V0.Y && o.V1.Y <= line.V0.Y))
                    {
                        System.Diagnostics.Trace.WriteLine(string.Format("{0}-----{1}", line.V0.ToString(), line.V1.ToString()));
                        line.V0.refCount--;
                        line.V1.refCount--;
                        lnAll.Remove(line);
                    }
                }
                //删除最左边
                if (lnAll.Contains(line))
                {
                    List<Line> tmp = lnAll.Where(o => o != line && line.V0.Y >= Math.Min(o.V0.Y, o.V1.Y) && line.V0.Y <= Math.Max(o.V0.Y, o.V1.Y)
                        || line.V1.Y >= Math.Min(o.V0.Y, o.V1.Y) && line.V1.Y <= Math.Max(o.V0.Y, o.V1.Y)).ToList();
                    if (tmp.All(o => o.V0.X >= line.V1.X && o.V0.X >= line.V1.X && o.V1.X >= line.V0.X && o.V1.X >= line.V0.X))
                    {
                        System.Diagnostics.Trace.WriteLine(string.Format("{0}-----{1}", line.V0.ToString(), line.V1.ToString()));
                        line.V0.refCount--;
                        line.V1.refCount--;
                        lnAll.Remove(line);
                    }
                }
            }
        }
        public void RecursionLine(List<Line> ln,List<List<Point>> area, List<List<Point>> areaNotClose)
        {
            int iCount = -1;
            //查找起点
            Line lastLn = null, headLn = null;
            //List<Point> ptExcept = area.Concat(areaNotClose).SelectMany(o => o).ToList();
            while (iCount++ < ln.Count-1)
            {
                if (ln[iCount].V0.refCount>1&&ln[iCount].V1.refCount>1)
                {
                    if (headLn == null || (ln[iCount].V0.X <= headLn.V0.X && ln[iCount].V1.X <= headLn.V1.X))
                        //if (!ptExcept.Contains(ln[iCount].V0) || !ptExcept.Contains(ln[iCount].V1))
                            headLn = ln[iCount];
                }
                else
                {
                    //删除
                    ln[iCount].V0.refCount--;
                    ln[iCount].V1.refCount--;
                    System.Diagnostics.Trace.WriteLine(string.Format("{0}-----{1}", ln[iCount].V0.ToString(), ln[iCount].V1.ToString()));
                    ln.RemoveAt(iCount);
                    iCount--;
                }
            }
            //最小起点包含最大点退出
            if (headLn == null)
                return;
            List<Line> lstLine = new List<Line>();
            List<Point> lstPt = new List<Point>();
            area.Add(lstPt);
            lastLn = headLn;
            if (headLn.V0.refCount + headLn.V1.refCount > 3)
            {//保证起点顺时针
                lstPt.Add(headLn.V0);
                lstPt.Add(headLn.V1);
                lstLine.Add(headLn);
                if (headLn.V0.X == headLn.V1.X)
                {
                    if (headLn.V0.Y > headLn.V1.Y)
                    {
                        lstPt.Reverse();
                    }
                }
                else
                {
                    if (headLn.V0.X > headLn.V1.X)
                    {
                    }
                    else
                    {
                    }
                }
            }

            List<Line> tmpLine = new List<Line>() { headLn };
            int iAll = -1,iRepeat = 0;//重复两次结束
            while(iAll++<ln.Count-1)
            {
                Point lastPt = lstPt.Last();
                Point ptTmp = ln[iAll].getSamePoint(lastLn);
                if (ln[iAll] != headLn && ptTmp == lastPt)
                {
                    //避免重复处理
                    if (tmpLine.Contains(ln[iAll]))
                        continue;

                    if(ln[iAll].V0.refCount + ln[iAll].V1.refCount <=3)
                    {
                        //删除
                        ln[iAll].V0.refCount--;
                        ln[iAll].V1.refCount--;
                        System.Diagnostics.Trace.WriteLine(string.Format("{0}-----{1}", ln[iAll].V0.ToString(), ln[iAll].V1.ToString()));
                        ln.RemoveAt(iAll);
                        iAll--;
                    }
                    else if (ln[iAll].V0.refCount + ln[iAll].V1.refCount >= 4)
                    {
                        bool bSalientPt = false;
                        int iTemp = iAll;
                        double dCross = ln[iAll].Cross(ln[iAll].V0 == ptTmp ? ln[iAll].V1 : ln[iAll].V0, lastLn.V0 == ptTmp ? lastLn.V1 : lastLn.V0, ptTmp)/ (ln[iAll].Distance * lastLn.Distance);
                        double dDot = ln[iAll].Dot(ln[iAll].V0 == ptTmp ? ln[iAll].V1 : ln[iAll].V0, lastLn.V0 == ptTmp ? lastLn.V1 : lastLn.V0, ptTmp)/ (ln[iAll].Distance * lastLn.Distance);
                        while (iTemp++ < ln.Count - 1)
                        {
                            //避免重复处理
                            if (tmpLine.Contains(ln[iTemp]))
                                continue;

                            Point ptTp = ln[iTemp].getSamePoint(lastLn);
                            if (ptTp == lastPt)
                            {
                                double dCrossTmp = ln[iTemp].Cross(ln[iTemp].V0 == ptTp ? ln[iTemp].V1 : ln[iTemp].V0, lastLn.V0 == ptTp ? lastLn.V1 : lastLn.V0, ptTp) / (ln[iTemp].Distance * lastLn.Distance);
                                double dDotTmp = ln[iTemp].Dot(ln[iTemp].V0 == ptTp ? ln[iTemp].V1 : ln[iTemp].V0, lastLn.V0 == ptTp ? lastLn.V1 : lastLn.V0, ptTp) / (ln[iTemp].Distance * lastLn.Distance);
                                //方向相同最小角度,方向相反最大角,优先取凸点
                                if (bSalientPt == false && dCross >=0&&dCrossTmp>=0)
                                {
                                    if (dDot > dDotTmp)
                                    {
                                        iAll = iTemp;
                                        dCross = dCrossTmp;
                                        dDot = dDotTmp;
                                    }
                                }
                                else if (dCross >=0&&dCrossTmp<0)
                                {
                                    bSalientPt = true;
                                    iAll = iTemp;
                                    dCross = dCrossTmp;
                                    dDot = dDotTmp;
                                }
                                else if (bSalientPt==false && dCross <0&&dCrossTmp>=0)
                                {
                                }
                                else
                                {
                                    bSalientPt = true;
                                    if (dDot < dDotTmp)
                                    {
                                        iAll = iTemp;
                                        dCross = dCrossTmp;
                                        dDot = dDotTmp;
                                    }
                                }
                            }
                        }

                        lstLine.Add(ln[iAll]);
                        tmpLine.Add(ln[iAll]);
                        ptTmp = ln[iAll].getSamePoint(lastLn);
                        lastLn = ln[iAll];
                        if (headLn.getSamePoint(lastLn) != null)
                            iRepeat++;
                        if (iRepeat > 1)
                            break;

                        //添加另外一个点
                        if (ptTmp == ln[iAll].V0)
                            lstPt.Add(ln[iAll].V1);
                        else
                            lstPt.Add(ln[iAll].V0);
                        iAll = -1;//重新开始
                    }
                }
            }

            if(lstPt.Count>0)
                DeleteLine(ln, lstLine);
            //区域不闭合
            if (iRepeat <= 1)
            {
                area.Remove(lstPt);
                areaNotClose.Add(lstPt);
            }

            //继续找另一个区域
            //if (tmpLine.Count > 0)
            if (ln.Count > 0)
                RecursionLine(ln, area, areaNotClose);                
        }
        public override IEnumerable<XElement> ToXml()
        {
            int idx = 0;
            foreach (Area ar in m_Areas)
                yield return new XElement("_" + idx++, ar.ToXml());
        }
    }
}
